package cn.fl.system.utils;

import cn.cmm.base.framework.utils.BaseLogUtils;
import cn.cmm.base.framework.utils.StringUtils;
import cn.fl.system.constant.SysLogConstant;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;

public class FileTypeUtil {

    public final static Map<String, String> FILE_TYPE_MAP = new HashMap<>();
    public final static Map<String, String> EXT_MAP = new HashMap<>();

    private static final Logger logger = LoggerFactory.getLogger(FileTypeUtil.class);

    private FileTypeUtil(){}
    static{
        getAllFileType(); //初始化文件类型信息
        getExtMap();
    }

    private static void getAllFileType()
    {
        FILE_TYPE_MAP.put("jpg", "FFD8FF"); //JPEG (jpg)
        FILE_TYPE_MAP.put("jpeg", "FFD8FF"); //JPEG (jpg)
        FILE_TYPE_MAP.put("png", "89504E47");  //PNG (png)
        FILE_TYPE_MAP.put("rtf", "7B5C727466");  //Rich Text Format (rtf)
        FILE_TYPE_MAP.put("xml", "3C3F786D6C");
        FILE_TYPE_MAP.put("zip", "504B0304");
        FILE_TYPE_MAP.put("rar", "52617221");
        FILE_TYPE_MAP.put("xls", "D0CF11E0");  //MS Word
        FILE_TYPE_MAP.put("xlsx", "504B0304");  //MS Word
        FILE_TYPE_MAP.put("doc", "D0CF11E0");  //MS Excel 注意：word 和 excel的文件头一样
        FILE_TYPE_MAP.put("docx", "504B0304");  //MS Excel 注意：word 和 excel的文件头一样
        FILE_TYPE_MAP.put("pdf", "255044462D312E");  //Adobe Acrobat (pdf)
        FILE_TYPE_MAP.put("mp4", "000000");  //MP4
    }

    private static void getExtMap()
    {
        EXT_MAP.put("1", "jpg"); //JPEG (jpg)
        EXT_MAP.put("2","jpeg"); //JPEG (jpg)
        EXT_MAP.put("3","png");  //PNG (png)
        EXT_MAP.put("4","gif");  //GIF (gif)
        EXT_MAP.put("5","tif");  //TIFF (tif)
        EXT_MAP.put("6","bmp"); //Windows Bitmap (bmp)
        EXT_MAP.put("7","dwg"); //CAD (dwg)
        EXT_MAP.put("8","html");  //HTML (html)
        EXT_MAP.put("9","rtf");  //Rich Text Format (rtf)
        EXT_MAP.put("10","xml");
        EXT_MAP.put("11","zip");
        EXT_MAP.put("12","rar");
        EXT_MAP.put("13","psd");  //Photoshop (psd)
        EXT_MAP.put("14","eml");  //Email [thorough only] (eml)
        EXT_MAP.put("17","xls");  //MS Word
        EXT_MAP.put("18","xlsx");  //MS Word
        EXT_MAP.put("19","doc");  //MS Excel 注意：word 和 excel的文件头一样
        EXT_MAP.put("20","docx");  //MS Excel 注意：word 和 excel的文件头一样
        EXT_MAP.put("25","pdf");  //Adobe Acrobat (pdf)
        EXT_MAP.put("28","wav");  //Wave (wav)
        EXT_MAP.put("29","avi");
        EXT_MAP.put("30","ram");  //Real Audio (ram)
        EXT_MAP.put("31","rm");  //Real Media (rm)
        EXT_MAP.put("32","mpg");  //
        EXT_MAP.put("34","asf"); //Windows Media (asf)
        EXT_MAP.put("35","mid");  //MIDI (mid)
        EXT_MAP.put("36","mp4");  //MP4
        EXT_MAP.put("37","txt");  //txt
        EXT_MAP.put("38","apk");  //apk
        EXT_MAP.put("39","ipa");  //ipa
    }

    /**
     * 得到上传文件的文件头
     * @param src
     * @return
     */
    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder();
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     * 根据制定文件的文件头判断其文件类型
     * @param filePath
     * @return
     */
    public static String getFileTypeByPath(String filePath){
        String res = "";
        try {
            FileInputStream is = new FileInputStream(filePath);
            byte[] b = new byte[10];
            is.read(b, 0, b.length);
            String fileCode = bytesToHexString(b);
            for(String key : FILE_TYPE_MAP.keySet()){
                String value = FILE_TYPE_MAP.get(key);
                if(value.toLowerCase().startsWith(fileCode.toLowerCase()) || fileCode.toLowerCase().startsWith(value.toLowerCase())){
                    res = key;
                    break;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return res;
    }

    public static List<String> getFileTypeByStream(byte[] bytes){
        List<String> list = Lists.newArrayList();
        String fileCode = bytesToHexString(bytes);
        BaseLogUtils.info(logger, "getFileTypeByStream", "FILE_TYPE_CODE ：" + fileCode.substring(0,8));
        //这种方法在字典的头代码不够位数的时候可以用但是速度相对慢一点
        for(String key : FILE_TYPE_MAP.keySet()){
            String value = FILE_TYPE_MAP.get(key);
            if(value.toLowerCase().startsWith(fileCode.toLowerCase()) || fileCode.toLowerCase().startsWith(value.toLowerCase())){
                list.add(key);
            }
        }
        BaseLogUtils.info(logger, "getFileTypeByStream", "GET_FILE_TYPE_BY_STREAM ：" + list);
        return list;
    }

    public static Map<String, String> judgeFileType(MultipartFile file) throws Exception {

        BaseLogUtils.info(logger, "judgeFileType", "JUDGE_FILE_TYPE 开始：" + file.getOriginalFilename());
        Map<String, String> result = new HashMap<>();
        result.put("code", "200");
        result.put("info", "成功");
        try {
            String originalFilename = file.getOriginalFilename();
            if (StringUtils.isNotEmpty(originalFilename)) {
                String _ext = originalFilename.substring(originalFilename.lastIndexOf("."));
                if (StringUtils.isEmpty(_ext)) {
                    BaseLogUtils.info(logger, "judgeFileType", "文件格式不正确，请重新上传");
                    result.put("code", "400");
                    result.put("info", "文件格式不正确，请重新上传");
                    return result;
                }
                String substringExt = _ext.substring(1).toLowerCase();
                if (!FileTypeUtil.EXT_MAP.containsValue(substringExt)) {
                    BaseLogUtils.info(logger, "judgeFileType", "不允许上传文件类型为" + _ext + "的文件，请重新上传");
                    result.put("code", "400");
                    result.put("info", "不允许上传文件类型为" + _ext + "的文件，请重新上传");
                    return result;
                }
                if (FILE_TYPE_MAP.keySet().contains(substringExt)) {
                    List<String> stringList = getFileTypeByStream(file.getBytes());
                    if (!stringList.contains(substringExt)) {
                        BaseLogUtils.info(logger, "judgeFileType", "实际文件内容格式与文件后缀" + substringExt + "不符，请重新上传");
                        result.put("code", "400");
                        result.put("info", "实际文件内容格式与文件后缀" + substringExt + "不符，请重新上传");
                        return result;
                    }
                }
                if (originalFilename.contains("%00") || originalFilename.split("\\.").length>2) {
                    BaseLogUtils.info(logger, "judgeFileType", "文件格式不正确，请重新上传");
                    result.put("code", "400");
                    result.put("info", "文件格式不正确，请重新上传");
                    return result;
                }
            } else {
                result.put("code", "400");
                result.put("info", SysLogConstant.LOG_FILE_UPLOAD_FAIL);
            }
        } catch (Exception e) {
            result.put("code", "400");
            result.put("info", SysLogConstant.LOG_FILE_UPLOAD_FAIL);
            e.printStackTrace();
            BaseLogUtils.info(logger, "judgeFileType", "JUDGE_FILE_TYPE 异常：" + e.getMessage());
        }
        BaseLogUtils.info(logger, "judgeFileType", "JUDGE_FILE_TYPE 结束：" + result);
        return result;
    }
}
